package com.ibeeking.found.msg.rest.service.impl;

import com.ibeeking.found.common.constants.WxMpConstant;
import com.ibeeking.found.common.utils.NumberUtils;
import com.ibeeking.found.common.utils.UserUtils;
import com.ibeeking.found.msg.rest.service.*;
import com.ibeeking.found.msg.service.common.bo.WxMpSourceBO;
import com.ibeeking.found.msg.service.common.bo.WxMpSourceDetailBO;
import com.ibeeking.found.msg.service.common.constants.RedisKeyConstant;
import com.ibeeking.found.msg.service.common.dos.WxMpFansDO;
import com.ibeeking.found.msg.service.common.dos.WxMpGroupMessageCountDO;
import com.ibeeking.found.msg.service.common.dos.WxMpGroupMessageDO;
import com.ibeeking.found.msg.service.common.param.WxMpGroupMessageParam;
import com.ibeeking.found.msg.service.common.param.WxMpGroupMessageUploadParam;
import com.ibeeking.found.msg.service.common.po.WxMpGroupUserMessagePO;
import com.ibeeking.found.msg.service.common.query.WxMpFansPageQuery;
import com.ibeeking.found.msg.service.common.query.WxMpGroupMessagePageQuery;
import com.ibeeking.found.msg.service.common.vo.WxMpSourceVO;
import com.ibeeking.found.msg.service.common.vo.WxMpGroupMessagePageVO;
import com.ibeeking.found.msg.service.common.vo.WxMpGroupMessageUploadVO;
import com.ibeeking.found.msg.service.common.vo.WxMpGroupUserMessageVO;
import com.ibeeking.found.msg.service.mapper.WxMpGroupMessageMapper;
import com.ibeeking.nematos.exception.BusinessException;
import com.ibeeking.nematos.log.utils.LogUtils;
import com.ibeeking.nematos.redis.utils.RedisUtils;
import com.ibeeking.nematos.utils.bean.BeanUtil;
import com.ibeeking.nematos.utils.data.Collections3;
import com.ibeeking.nematos.utils.data.StringUtils;
import com.ibeeking.nematos.utils.json.JsonUtils;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpMassMessageService;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.WxMpMassNews;
import me.chanjar.weixin.mp.bean.WxMpMassVideo;
import me.chanjar.weixin.mp.bean.material.WxMpNewsArticle;
import me.chanjar.weixin.mp.bean.result.WxMpMassSendResult;
import me.chanjar.weixin.mp.bean.result.WxMpMassUploadResult;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @ClassName WxMpGroupMessageServiceImpl
 * @Description
 * @Author ibeeking
 * @Date 2020-12-25 11:20
 **/
@Service
public class WxMpGroupMessageServiceImpl extends ServiceImpl<WxMpGroupMessageMapper, WxMpGroupMessageDO> implements IWxMpGroupMessageService {

    @Resource
    private IOpenService wxOpenServiceImpl;

    @Resource
    private WxMpGroupMessageMapper wxMpGroupMessageMapper;

    @Resource
    private IWxMpSourceService mpSourceService;
    @Resource
    private IWxMpGroupMessageCountService wxmpGroupMessageCountService;

    @Resource
    private RedisUtils redis;

    @Resource
    private IWxMpFansTagsService wxMpFansTagsServiceImpl;
    @Resource
    private IWxMpFansService wxMpFansServiceImpl;

    @Resource
    private IMsgService msgServiceImpl;

    @Resource
    private IWxMpSourceService wxMpSourceServiceImpl;

    @Override
    public IPage<WxMpGroupMessagePageVO> pageList(WxMpGroupMessagePageQuery pageQuery) {
        Page page = new Page(pageQuery.getPageNum(), pageQuery.getPageSize());
        IPage<WxMpGroupMessagePageVO> pages = this.wxMpGroupMessageMapper.pageList(page, pageQuery);
        return pages;
    }

    /***
     * 新增
     * @param param
     */
    @Override
    public Boolean add(WxMpGroupMessageUploadParam param) {

        //复制对象
        WxMpGroupMessageParam wxmpGroupMessageParam = BeanUtil.convertBean(param, WxMpGroupMessageParam.class);
        WxMpGroupMessageUploadParam.Message message = param.getMessage();
        //新类型转换为老类型
        message.setMessageTypeString(msgServiceImpl.newTypeStr2OldTypeStr(message.getMenuSourceType()));
        message.setMessageType(msgServiceImpl.newType2OldType(message.getMenuSourceType()));
        if (WxMpConstant.MESSAGE_TEXT.equals(message.getMessageTypeString())) {
            message.setContent(message.getUnionKey());
        } else {
            message.setMediaId(message.getUnionKey());
        }
        BeanUtils.copyProperties(param.getMessage(), wxmpGroupMessageParam);
        //拼接数据
        WxMpFansPageQuery wxMpFansPageQuery = null;
        List<String> ids = null;
        if (param.getAllSend() != 1) {
            if ("1".equals(param.getSendType())) {
                ids = param.getTagIds();
            } else if ("2".equals(param.getSendType())) {
                if (Collections3.isNotEmpty(param.getFansObject().getUserIds())) {
                    ids = param.getFansObject().getUserIds();
                } else {
                    wxmpGroupMessageParam.setSendType("3");
                    wxMpFansPageQuery = param.getFansObject().getWxMpFansPageQuery();
                }
            }
        }
        return this.add(wxmpGroupMessageParam, wxMpFansPageQuery, ids);
    }

    /**
     * 发送方式有四种:
     * 1.勾选一部分人发送
     * 2.勾选标签发送
     * 3.根据查询的条件进行发送
     * 4.选择全部发送
     * 以上发送模式中出全部发送外，
     * 均必须发送两人以上 即人数 >= 2人
     * 每个openId每月最多只能接收4次,
     * 超过4次的粉丝可以过滤掉不用再发
     * 接口调用次数限制每分钟60次
     * 发送结束后并将发送的结果保存到本地，
     * 方便后续查询发送记录
     *
     * @param param
     * @param ids
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean add(WxMpGroupMessageParam param, WxMpFansPageQuery pageQuery, List<String> ids) {
        Long userId = UserUtils.getUserId();
        //获取当日发送次数
        String mpMessageSendCountInRedisKey = RedisKeyConstant.MODULE + RedisKeyConstant.MP_MESSAGE_SEND_COUNT
                //+ wxmpGroupMessageParam.getMpId() + "_" + DateUtils.parseStr(LocalDate.now(), "DD");
                + param.getAppId() + "_" + LocalDate.now().getDayOfMonth();


        WxMpGroupMessageDO wxmpGroupMessageDO = BeanUtil.convertBean(param, WxMpGroupMessageDO.class);
        wxmpGroupMessageDO.setCreateBy(userId);
        wxmpGroupMessageDO.setCreateTime(LocalDateTime.now());
        wxmpGroupMessageDO.setModifyTime(LocalDateTime.now());
        if (this.wxMpGroupMessageMapper.insert(wxmpGroupMessageDO) > 0 == false) {
            return false;
        }

        //取出全部发送
        if (param.getAllSend() == 1) {
            return this.sendForAll(param, wxmpGroupMessageDO, mpMessageSendCountInRedisKey, ids);
        }
        List<WxMpFansDO> fans = this.getWxMpFans(param.getSendType(), ids, pageQuery, param.getAppId());
        //非空判断
        if (Collections3.isEmpty(fans)) {
            throw new BusinessException("没有可发送的人");
        }
        //过滤本月接收已经超出四次的粉丝
        try {
            fans = fans.stream().filter(account -> account.getSendCount() == null || account.getSendCount() < 4).collect(Collectors.toList());
        } catch (NullPointerException e) {
            throw new BusinessException("没有可发送的人");
        }

        if (Collections3.isEmpty(fans)) {
            throw new BusinessException("没有可发送的人");
        } else if (fans.size() < 2) {
            throw new BusinessException("可发送人数不满足2人及以上");
        }

        WxMpMassUploadResult wxMpMassUploadResult = null;
        try {
            if (WxConsts.MediaFileType.VIDEO.equals(param.getMessageTypeString())) {
                wxMpMassUploadResult = this.getUpload(param);
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException("发送失败");
        }
        msgServiceImpl.sendMessageByOpenId(fans, param, wxMpMassUploadResult, wxmpGroupMessageDO, mpMessageSendCountInRedisKey);
        return true;
    }


    /**
     * 页面接口修改数据
     *
     * @param wxmpGroupMessageParam
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean modify(WxMpGroupMessageParam wxmpGroupMessageParam) {
        Long userId = UserUtils.getUserId();
        WxMpGroupMessageDO wxmpGroupMessageDO = BeanUtil.convertBean(wxmpGroupMessageParam, WxMpGroupMessageDO.class);
        wxmpGroupMessageDO.setModifyBy(userId);
        wxmpGroupMessageDO.setModifyTime(LocalDateTime.now());
        return wxMpGroupMessageMapper.updateById(wxmpGroupMessageDO) > 0;
    }


    /**
     * 修改提交发送数据后回调修改获取的发送结果保存
     *
     * @param wxmpGroupMessageDO
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean modify(WxMpGroupMessageDO wxmpGroupMessageDO) {
        Long userId = UserUtils.getUserId();
        wxmpGroupMessageDO.setModifyBy(userId);
        wxmpGroupMessageDO.setModifyTime(LocalDateTime.now());
        return wxMpGroupMessageMapper.updateById(wxmpGroupMessageDO) > 0;
    }

    @Override
    public WxMpGroupMessageUploadVO view(Long id) {
        WxMpGroupMessageDO wxmpGroupMessageDO = wxMpGroupMessageMapper.selectById(id);
        WxMpGroupMessageUploadVO vo = new WxMpGroupMessageUploadVO();
        vo.setAppId(wxmpGroupMessageDO.getAppId());
        vo.setId(wxmpGroupMessageDO.getId());
        vo.setAllSend(wxmpGroupMessageDO.getAllSend());
        vo.setSendIgnoreReprintl(wxmpGroupMessageDO.getSendIgnoreReprint());
        vo.setRemark(wxmpGroupMessageDO.getRemark());
        vo.setSendType(wxmpGroupMessageDO.getSendType());
        WxMpGroupMessageUploadVO.Message message = new WxMpGroupMessageUploadVO.Message();
        message.setSourceType(msgServiceImpl.OldType2NewType(wxmpGroupMessageDO.getMessageType()));
        message.setAppId(wxmpGroupMessageDO.getAppId());
        if (Integer.valueOf(5).equals(wxmpGroupMessageDO.getMessageType())) {
            message.setUnionKey(wxmpGroupMessageDO.getContent());
        } else {
            message.setUnionKey(wxmpGroupMessageDO.getMediaId());
            WxMpSourceVO sourceVO = this.mpSourceService.queryByMediaId(wxmpGroupMessageDO.getMediaId());
            if (sourceVO != null) {
                if (Integer.valueOf(1).equals(wxmpGroupMessageDO.getMessageType())) {
                    if (sourceVO != null) {
                        message.setSourceDetails(sourceVO.getSourceDetail());
                    } else {
                        message.setSourceDetails(new ArrayList());
                    }
                } else {
                    message.setRepName(sourceVO.getName());
                    if (StringUtils.isNotBlank(sourceVO.getPath())) {
                        message.setRepUrl(sourceVO.getPath());
                    } else {
                        message.setRepUrl("");
                    }
                }
            }
        }
        vo.setMessage(message);
        return vo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean del(Long id) {

        WxMpGroupMessageDO wxmpGroupMessageDO = this.wxMpGroupMessageMapper.selectById(id);
        if (wxmpGroupMessageDO == null) {
            return true;
        }
        QueryWrapper<WxMpGroupMessageCountDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(WxMpGroupMessageCountDO::getGroupMessageId, id);
        List<WxMpGroupMessageCountDO> list = this.wxmpGroupMessageCountService.list(queryWrapper);
        if (list == null) {
            return true;
        }
        Long tenantId = UserUtils.getTenantID();
        WxMpService wxMpService = wxOpenServiceImpl.getWxOpenMpService(tenantId);
        if(wxMpService == null) throw new BusinessException("公众号没有授权!");
        Long msgId = Long.valueOf(list.get(0).getMsgId());
        /**
         * 接口原文:
         * 删除群发.
         * 群发之后，随时可以通过该接口删除群发。
         * 请注意：
         * 1、只有已经发送成功的消息才能删除
         * 2、删除消息是将消息的图文详情页失效，已经收到的用户，还是能在其本地看到消息卡片。
         * 3、删除群发消息只能删除图文消息和视频消息，其他类型的消息一经发送，无法删除。
         * 4、如果多次群发发送的是一个图文消息，那么删除其中一次群发，就会删除掉这个图文消息也，导致所有群发都失效
         */
        try {
            WxMpMassMessageService wxMpMassMessageService = wxMpService.getMassMessageService();
            if(wxMpMassMessageService == null) throw new BusinessException("公众号没有授权!");
            wxMpMassMessageService.delete(msgId, null);
        } catch (Exception e) {
            throw new BusinessException(e.getMessage());
        }
        list.stream().forEach(count -> {
            this.wxmpGroupMessageCountService.del(count.getId());
        });
        return wxMpGroupMessageMapper.deleteById(id) > 0;
    }

    /**
     * 查询公众号用户消息
     *
     * @param pageQuery
     * @return
     */
    @Override
    public IPage<WxMpGroupUserMessageVO> queryUserMessage(WxMpGroupMessagePageQuery pageQuery) {
        Page<WxMpGroupUserMessageVO> page = new Page<>(pageQuery.getPageNum(), pageQuery.getPageSize());
        WxMpGroupUserMessagePO po = BeanUtil.convertBean(pageQuery, WxMpGroupUserMessagePO.class);
        IPage<WxMpGroupUserMessageVO> wxmpGroupMessageVOS = this.wxMpGroupMessageMapper.queryUserMessage(page, po);
        return wxmpGroupMessageVOS;
    }

    private List<WxMpFansDO> getWxMpFans(String type, List<String> ids, WxMpFansPageQuery pageQuery, String appId) {

        switch (type) {
            case "1":
                return getWxMpFansByTag(ids, appId);
            case "2":
                return ids.stream().map(id -> new WxMpFansDO().setOpenId(id)).collect(Collectors.toList());
            case "3":
                return JsonUtils.jsonToList(this.wxMpFansServiceImpl.queryFansId(pageQuery), WxMpFansDO.class);
            default:
                return null;
        }

    }

    private List<WxMpFansDO> getWxMpFansByTag(List<String> ids, String appId) {
        List<Long> tags = ids.stream().map(n -> Long.valueOf(n)).collect(Collectors.toList());
        return this.wxMpFansTagsServiceImpl.queryFansByTagsIds(tags, appId);
    }

    /**
     * 发送给所有人
     *
     * @return
     */
    private Boolean sendForAll(WxMpGroupMessageParam param, WxMpGroupMessageDO wxmpGroupMessageDO, String key, List<String> ids) {
        if (param.getMessageType() == 1) {
            return sendNews(param, wxmpGroupMessageDO, key);
        } else if (param.getMessageType() == 4) {
            return sendMedia(param, wxmpGroupMessageDO, key);
        } else {
            //SDK未提供调用方法 需要自己调用构造器实现
            return sendMessage(param, wxmpGroupMessageDO, ids, key);
        }
    }

    /**
     * 发送类型为多媒体类型(全部发送)
     *
     * @param param
     * @return
     */
    private Boolean sendMedia(WxMpGroupMessageParam param, WxMpGroupMessageDO wxmpGroupMessageDO, String key) {

        String lockKey = RedisKeyConstant.MODULE + "lockKey";

        try {
            Integer count = 0;
            //获取锁
            boolean lock = false;
            while (lock == false) {
                lock = this.redis.tryLock(lockKey, 5, 40);
            }
            if (lock) {
                count = msgServiceImpl.getCount(key);
                if (count >= 100) {
                    throw new BusinessException("今日可发送的次数已使用完");
                }
            } else {
                return false;
            }
            //1.首先上传素材
            WxMpMassUploadResult mass = this.getUpload(param);
            param.setMediaId(mass.getMediaId());
            WxMpMassSendResult sendResult = this.msgServiceImpl.massMessageSend(param.getAppId(), msgServiceImpl.getJsonString(param, null), true);
            if (sendResult != null) {
                //增加一次发送次数
                count = count + 1;
                //保存今日发送次数,并设置失效时间
                redis.set(key, count, RedisKeyConstant.DAY);
                this.msgServiceImpl.saveSendInfo(0, sendResult, wxmpGroupMessageDO, param.getMessageType(), null);
                return true;
            } else {
                throw new BusinessException("网络异常");
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException(e.getMessage());
        } finally {
            try {
                redis.unlock(lockKey);
            } catch (Exception e) {
                log.error("释放锁异常:{}", e);
                e.printStackTrace();
            }
        }

    }

    /**
     * 发送类型为图文类型(全部发送)
     *
     * @return
     */
    private Boolean sendNews(WxMpGroupMessageParam param, WxMpGroupMessageDO wxmpGroupMessageDO, String key) {
        String lockKey = RedisKeyConstant.MODULE + "lockKey";
        try {
            Integer count = 0;
            //获取锁
            boolean lock = false;
            while (lock == false) {
                lock = this.redis.tryLock(lockKey, 5, 40);
            }
            if (lock) {
                count = msgServiceImpl.getCount(key);
                if (count >= 100) {
                    throw new BusinessException("今日可发送的次数已使用完");
                }
            } else {
                return false;
            }

            String newsJson = msgServiceImpl.getJsonString(param, null);
            WxMpMassSendResult sendResult = msgServiceImpl.massMessageSend(param.getAppId(), newsJson, true);
            if (sendResult != null) {
                //增加一次发送次数
                count = count + 1;
                //保存今日发送次数,并设置失效时间
                redis.set(key, count, RedisKeyConstant.DAY);
                //给每个月接收次数小于4次的用户增加一次获取消息
                this.msgServiceImpl.saveSendInfo(0, sendResult, wxmpGroupMessageDO, param.getMessageType(), null);
                return true;
            } else {
                throw new BusinessException("网络异常");
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException(e.getMessage());
        } finally {
            try {
                redis.unlock(lockKey);
            } catch (Exception e) {
                log.error("释放锁异常:{}", e);
                e.printStackTrace();
            }
        }
    }

    /**
     * 生成视频素材
     *
     * @param param
     * @return
     */
    private WxMpMassVideo getMedia(WxMpGroupMessageParam param) {
        //1.首先上传素材
        //1.1从库中取出素材
        WxMediaUploadResult result = this.mpSourceService.getTempMedia(NumberUtils.string2DecimalNullValue2Zero(param.getMediaId()).longValue(), param.getMessageTypeString(), param.getAppId());
        if (StringUtils.isBlank(result.getMediaId())) {
            LogUtils.error("获取MediaId失败 , result:[ mediaId : {} , ]", result.getMediaId());
            throw new BusinessException("请求资源失败");
        }
        //从素材库上传video
        WxMpMassVideo video = new WxMpMassVideo();
        video.setMediaId(result.getMediaId());
        //video.setTitle(rescouseDO.getSubject());
        //video.setDescription(rescouseDO.getContent());
        return video;
    }

    /**
     * 生成图文素材
     *
     * @param param
     * @return
     */
    private WxMpMassNews getNews(WxMpGroupMessageParam param) {
        //获取图文
        WxMpSourceBO wxMpSourceBO = wxMpSourceServiceImpl.querySourceById(param.getBatchNo());
        if (null == wxMpSourceBO) {
            return null;
        }
        WxMpMassNews massNews = new WxMpMassNews();
        //上传所需对象
        for (WxMpSourceDetailBO vo : wxMpSourceBO.getSourceDetail()) {
            //获取本公众号端的缩略图id
            WxMpNewsArticle article = BeanUtil.convertBean(vo, WxMpNewsArticle.class);
            if (null != vo.getThumbMediaId()) {
                String s = wxMpSourceServiceImpl.uploadMediaToWx(vo.getThumbMediaId());
                article.setThumbMediaId(s);
            }
            massNews.addArticle(article);
        }
        return massNews;
    }

    private WxMpMassUploadResult getUpload(WxMpGroupMessageParam param) throws WxErrorException {
        Long tenantId = UserUtils.getTenantID();
        WxMpService wxMpService = wxOpenServiceImpl.getWxOpenMpService(tenantId);

        if (param.getMessageType() == 1) {
            WxMpMassNews massNews = getNews(param);
            return wxMpService.getMassMessageService().massNewsUpload(massNews);
        } else {
            WxMpMassVideo media = getMedia(param);
            WxMpMassUploadResult result = wxMpService.getMassMessageService().massVideoUpload(media);
            //result.setMediaId(media.getMediaId());
            return result;
        }
    }


    private Boolean sendMessage(WxMpGroupMessageParam param, WxMpGroupMessageDO wxmpGroupMessageDO, List<String> ids, String key) {

        String jsonData = this.msgServiceImpl.getJsonString(param, ids);
        boolean lock = false;
        Integer count = 0;
        String lockKey = RedisKeyConstant.MODULE + "lockKey";
        try {
            //获取锁
            while (!lock) {
                lock = this.redis.tryLock(lockKey, 5, 40);
            }
            if (lock) {
                count = msgServiceImpl.getCount(key);
                if (count >= 100) {
                    throw new BusinessException("今日可发送的次数已使用完");
                }
            } else {
                return false;
            }
            WxMpMassSendResult result = this.msgServiceImpl.massMessageSend(param.getAppId(), jsonData, true);
            if (result != null) {
                count = count + 1;
                redis.set(key, count, RedisKeyConstant.DAY);
                this.msgServiceImpl.saveSendInfo(0, result, wxmpGroupMessageDO, param.getMessageType(), null);
                return true;
            } else {
                throw new BusinessException("网络异常");
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException(e.getMessage());
        } finally {
            try {
                redis.unlock(lockKey);
            } catch (Exception e) {
                log.error("释放锁异常:{}", e);
                e.printStackTrace();
            }
        }

    }


}
